import processing.core.*;
import processing.opengl.*;
import traer.physics.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.awt.Color;

class HTriangle {

    // Graphical properties
    private Color baseColor;
    private Color lightColor;
    private boolean highlight;	// Like "selected" or something.
				// Happens when the traveler is inside
				// the triangle boundaries.
    private PApplet parent;	// Used to render the triangle and get convenience funcs.

    // MISCH. properties
    private int age;			// Used to prevent "triangle flashing"
    private HParticle p1, p2, p3;	// Vertices

    /**
     * CONSTRUCTOR
     * */
    public HTriangle(PApplet parent, HParticle p1, HParticle p2, HParticle p3) {
	this.p1 = p1;
	this.p2 = p2;
	this.p3 = p3;

	this.parent = parent;
    }

    public void setHighlighted( boolean highlighted ) {
	this.highlight = highlighted;
    }

    public int getAge() {
	return age;
    }
	
    public void setColor(int r, int g, int b) {
	baseColor = new Color(r, g, b);
	// lightColor = new Color( parent.constrain(r + 75, 0, 255),
	// 			parent.constrain(g + 75, 0, 255),
	// 			parent.constrain(b + 75, 0, 255) );
	lightColor = baseColor.brighter();	// This method exists?!
    }

    // public void setColor(int hue) {
    //     this.hue = hue;
    // }

    /**
     * test2
     *
     * This method was stolen from Google. Don't ask me!
     * */
    private int test2( double px, double py, double m, double b ) {    
	if (py < m * px + b ) {
	    return -1; // point is under line
	}else if ( py == m * px + b ){
	    return 0; // point is on line
	} else {
	    return 1; // point is over line
	}
    }

    /**
     * test1
     *
     * Builds on the private method test2 to check something.
     * Also totally stolen from Google (translated from the C language).
     * Don't ask me!
     * */
    private boolean test1(double px, double py, double m,double b, double lx,double ly) {     
	return (test2(px,py, m,b) == test2(lx,ly,m,b));    
    }

    /**
     * containsPoint( p )
     *
     * Returns whether or not the Point p is contained within this triangle.
     *
     * The math herein is taken from this page:
     * http://stackoverflow.com/questions/6187735/
     * if-point-is-inside-a-triangle-help-when-point-is-on-the-boundary-of-triangle
     * 
     * Translated from the C programming language.
     * */
    public boolean containsPoint (Point p) {

	double x0 = p1.getX();
	double y0 = p1.getY();
	double x1 = p2.getX();
	double y1 = p2.getY();
	double x2 = p3.getX();
	double y2 = p3.getY();

	double px = p.getX();
	double py = p.getY();

	boolean line1, line2, line3;    
	double m01 = (y1-y0)/(x1-x0);    
	double b01 = m01 * -x1 + y1;    
	double m02, m12, b02, b12;    
	m02 = (y2-y0)/(x2-x0);    
	m12 = (y2-y1)/(x2-x1);    
	b02 = m02 * -x2 + y2;    
	b12 = m12 * -x2 + y2;

	// vertical line checks
	if( x1 == x0 ) {    
	    line1 = ( (px <= x0) == (x2 <= x0) );    
	} else {    
	    line1 = test1( px, py, m01, b01,x2,y2);    
	}

	if( x1 == x2 ) {    
	    line2 = ( (px <= x2) == (x0 <= x2) );    
	} else {    
	    line2 = test1(px,py, m12, b12,x0,y0);    
	}

	if( x2 == x0 ) {    
	    line3 = ( (px <= x0 ) == (x1 <= x0) );
	} else {    
	    line3 = test1(px, py, m02,b02,x1,y1);    
	}

	return line1 && line2 && line3;
    }

    public ArrayList<HParticle> getPoints() {
	ArrayList<HParticle> ptList = new ArrayList<HParticle>();
	ptList.add(p1);
	ptList.add(p2);
	ptList.add(p3);
	return ptList;
    }

    // Returns the area of the triangle
    public double size() {
	// find longest edge
	double e1 = Math.hypot(p1.getX() - p2.getX(), p1.getY() - p2.getY());
	double e2 = Math.hypot(p2.getX() - p3.getX(), p2.getY() - p3.getY());
	double e3 = Math.hypot(p3.getX() - p1.getX(), p3.getY() - p1.getY());

	double theEdge = 0;
	HParticle thePoint = null;

	HParticle ep1, ep2;
	ep1 = ep2 = null;
	    
	if (Math.max(Math.max(e1, e2), e3) == e3) {
	    theEdge = e1;
	    ep1 = (p1.getX() < p2.getX()? p1 : p2);
	    ep2 = (p1.getX() >= p2.getX()? p1 : p2);
	    thePoint = p3;
	}
	else if (Math.max(Math.max(e1, e2), e3) == e2) {
	    theEdge = e2;
	    ep1 = (p3.getX() < p2.getX()? p3 : p2);
	    ep2 = (p3.getX() >= p2.getX()? p3 : p2);
	    thePoint = p1;
	}
	else if (Math.max(Math.max(e1, e2), e3) == e1) {
	    theEdge = e3;
	    ep1 = (p1.getX() < p3.getX()? p1 : p3);
	    ep2 = (p1.getX() >= p3.getX()? p1 : p3);
	    thePoint = p2;
	}

	// get slope
	if (ep1.getX() == ep2.getX())
	    return Math.abs(thePoint.getY() - ep2.getY());
	if (ep1.getY() == ep2.getY())
	    return Math.abs(thePoint.getX() - ep2.getX());
	    
	double slope = ((double)(ep1.getY() - ep2.getY()))/(ep1.getX() - ep2.getX());
	double perpSlope = -1.0 / slope;

	double eyi = ep1.getY() - slope*ep1.getY();
	double myi = thePoint.getY() - perpSlope*thePoint.getX();

	double xi = (eyi - myi)/((1 - (slope/perpSlope))*perpSlope);
	double yi = slope*xi + eyi;

	// distance from thePoint to theEdge:
	double perpDist = Math.hypot(xi - thePoint.getX(), yi - thePoint.getY());
	double area = (theEdge * perpDist)/2.0;
	return area;
    }

    public boolean equals(HTriangle other) {
	ArrayList<HParticle> otherPts = other.getPoints();
	ArrayList<HParticle> ptList = getPoints();

	Iterator<HParticle> oi = otherPts.iterator();
	while (oi.hasNext()) {
	    Iterator<HParticle> ti = ptList.iterator();
	    while (ti.hasNext()) {
		HParticle p1 = oi.next();
		HParticle p2 = ti.next();
		if (p1 == p2) {
		    oi.remove();
		    ti.remove();
		}
	    }
	}
	    
	return ptList.isEmpty() && otherPts.isEmpty();
    }

    public void render() {
	// increment age
	age++;

	// expensive! :
	// parent.colorMode(parent.HSB);

	int alpha = parent.constrain(100 - (int)((255.0/HMusic.TRIANGLE_THRESH) * size()),
				     0,
				     255);
	if (alpha < 0) alpha = 0;
	if (alpha > 255) alpha = 255;

	parent.stroke(150);
	parent.strokeWeight(0);

	if ( highlight )
	    parent.fill( lightColor.getRed(),
			 lightColor.getGreen(),
			 lightColor.getBlue(),
			 alpha );
	else
	    parent.fill( baseColor.getRed(),
			 baseColor.getGreen(),
			 baseColor.getBlue(),
			 alpha );

	parent.triangle(p1.getX(), p1.getY(),
			p2.getX(), p2.getY(),
			p3.getX(), p3.getY());


    }
	
}